#include "led.h"
/* 接口说明
大致工作原理:
    (1) A B C D 四条地址线通过两片TC7262组成了行选择器,让某一行的电源全部接通
    (2) 二进制 0bDCBA 代表具体的某一行
    (3) TC5020提供16个恒流驱动接口,根据R1或G1与CLK LAT的配合,将数据移位到移位寄存器
        决定导通某一列.
    (4) 由此,任何一个LED均可以导通,但同一时刻只能导通一行中的某一些LED灯珠.
    (5) 因为有两个TC7262可以选中16行中的某一行,所以,一屏显示需要分别接通16行,才能显示
        完整,所以是16扫.

地址线:
    A B C D
        (1) 地址线信号经74HC245缓冲后, 给了两片TC7262译码成16行
        (2) 当地址线60-70ms没有变动时, 芯片保护,输出关闭
    LAT
        (1) LAT 信号经74HC245送给了TC5020
        (2) 任何时候可以移位给TC5020设置数据
        (3) 当LAT由低向高跳变时,数据被锁存

    OE
        (1) OE 信号占了74HC245的两路
        (2) 一路送给了下一块板
        (2) 另一路送给了列选择器的３脚OE，OE=0时,输出关闭,显示关闭
 */

const GPIO_Init_Param_Typedef GPIO_HUB08[20] =
    {
        // 右边
        {GPIOB, {LL_GPIO_PIN_0, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // A
        {GPIOB, {LL_GPIO_PIN_2, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // B
        {GPIOB, {LL_GPIO_PIN_11, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0}, // C
        {GPIOB, {LL_GPIO_PIN_13, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0}, // D
        {GPIOB, {LL_GPIO_PIN_15, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0}, // G1
        {GPIOB, {LL_GPIO_PIN_4, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // G2
        {GPIOB, {LL_GPIO_PIN_6, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // LAT
        {GPIOB, {LL_GPIO_PIN_8, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // CLK
                                                                                                                // 左边
        {GPIOB, {LL_GPIO_PIN_1, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // GND
        {GPIOB, {LL_GPIO_PIN_10, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0}, // GND
        {GPIOB, {LL_GPIO_PIN_12, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0}, // GND
        {GPIOB, {LL_GPIO_PIN_14, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0}, // OE
        {GPIOB, {LL_GPIO_PIN_3, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // R1
        {GPIOB, {LL_GPIO_PIN_5, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // R2
        {GPIOB, {LL_GPIO_PIN_7, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // GND
        {GPIOB, {LL_GPIO_PIN_9, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // GND
                                                                                                                // 显示灯
        {GPIOA, {LL_GPIO_PIN_0, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // LED
        {0, {0, 0, 0, 0, 0}, 0}};

/* 缓冲区大小 */
uint16_t display_buf[16 * BUF_SIZE] = {0};

/* 全局变量,用于给中断程序分配任务 */
struct display_param d = {0};

/* 以下是一个定时器,到时间将tim1_count+1 */
volatile uint32_t tim1_count = 0;

// HUB08 双色接口 需要11根控制线.这里初始化这些IO口
void led_init()
{
    GPIO_Init((GPIO_Init_Param_Typedef *)GPIO_HUB08);
}

/* 将读取的字体放到显存中,position是位置0表示最左边,每加1表示向右移动8个像素 */
void insert_8x16_to_display_buf(uint8_t *buf, uint16_t position)
{
    uint8_t *p = (uint8_t *)display_buf + position;
    for (uint8_t i = 0; i < 16; i++)
    {
        *p = *buf;
        buf += 1;
        p += BUF_SIZE * 2;
    }
}

void insert_16x16_to_display_buf(uint8_t *buf, uint16_t position)
{
    insert_8x16_to_display_buf(buf, position);
    position = position == BUF_SIZE * 2 - 1 ? 0 : position + 1;
    insert_8x16_to_display_buf(buf + 16, position);
}

/* 清空显存内容 */
void clear_8x16_to_display_buf(uint16_t position)
{
    uint8_t *p = (uint8_t *)display_buf + position;
    for (uint8_t i = 0; i < 16; i++)
    {
        *p = 0;
        p += BUF_SIZE * 2;
    }
}

/* 读点阵数据,可以读字库中的,也可以读程序内置字库,
返回1表示是ascii 返回2表示,前题要要求编码是正确的
本程序不具备猜测纠正编码的功能 */
uint8_t read_font(uint8_t *buf, uint16_t GB2312_Code)
{
    uint32_t addr = 0;
    uint8_t region = GB2312_Code;
    uint8_t position = GB2312_Code >> 8;
    /* 1到9区的非汉字字符在Flash中读  16到87区的汉字在Flash中读
    这两部分本来在字库中是紧挨的,中间我插入了 6 * 94 * 32个0*/
    if (region >= 0xA1)
    {
        addr |= ((region - 0xA1) * 94 + (position - 0xA1)) * 32;
        flash_read_data(addr, buf, 32);
        return 2;
    }
    /* ASCII字符在程序中数组中读 */
    else if (region >= 0x20)
    {
        uint8_t *psrc = (uint8_t *)&ascii_8x16_font[((uint8_t)GB2312_Code - 0x20) * 16];
        for (uint8_t n = 0; n < 16; n++)
        {
            *buf++ = *psrc++;
        }
        return 1;
    }
    return 0;
}

/* 显示字符串 */
void display_text(uint8_t *text)
{
    // 创建显示任务,就不用管了,其余交给中断函数
    d.text_buf = text;
    d.head = 0;
    d.tail = 8;
    d.left_shift_time = MS_PER_SHIFT;
    d.display_nbit = 64;
    d.new_data_in = 1;
}

/* 显示跑表 */
void watch()
{
    const uint8_t tab[] = {0, 1, 3, 4};
    uint8_t time[] = {0x01, 0x01, 0x01, 0x01};
    volatile uint8_t new_time[4];
    uint8_t buf[32];
    uint32_t cur_count;
    // 初始化显存
    read_font(buf, ':');
    insert_8x16_to_display_buf(buf, 2);
    read_font(buf, 0xEBC3);
    insert_16x16_to_display_buf(buf, 6);
    // 更新显存
    tim1_count = 0;
    while (1)
    {
        cur_count = tim1_count;
        cur_count /= 2;
        for (uint8_t i = 3; i != 0xFF; i--)
        {
            // 从十位取起,即显示精度为0.01s
            cur_count /= 10;
            new_time[i] = cur_count % 10;
            if (new_time[i] != time[i])
            {
                insert_8x16_to_display_buf((uint8_t *)&ascii_8x16_font[(new_time[i] + '0' - 0x20) * 16], tab[i]);
                time[i] = new_time[i];
            }
        }
        if(tim1_count > 10 * 1000 * 2){
            return;
        }
    }
}

/* 定时器初始化 */
void TIM1_Init(void)
{
    LL_TIM_InitTypeDef TIM_InitStruct = {0};
    LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);
    NVIC_SetPriority(TIM1_UP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
    NVIC_EnableIRQ(TIM1_UP_IRQn);
    TIM_InitStruct.Autoreload = US_PER_ROW - 1; // 多少微秒刷新一行,刷新一行数据占用的的时间为27us左右
    TIM_InitStruct.Prescaler = 72 - 1;
    TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
    TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
    LL_TIM_Init(TIM1, &TIM_InitStruct);
    LL_TIM_DisableARRPreload(TIM1); /* 不缓存APR的数值,更改时直接写入并生效 */
    LL_TIM_DisableMasterSlaveMode(TIM1);
    LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL);
    LL_TIM_SetTriggerOutput(TIM1, LL_TIM_TRGO_UPDATE);
    LL_TIM_EnableIT_UPDATE(TIM1);
}

void TIM1_UP_IRQHandler()
{
    static uint16_t display_nbit_offset = 0;     // 偏移多少个bit显示
    static uint8_t r = 0;                        // 静态变量,记录行号
    static uint16_t display_nbit = SCREEN_WIDTH; // 要移位多少位到显示器
    static uint32_t head = 0;                    // 循环显存头的位置
    static uint32_t tail = 8;                    // 循环显存尾的位置
    static uint8_t *text_buf = 0;                // 存储文字
    static uint16_t left_shift_time = 0;         // 多久向左移一位

    // 清标志,计时变量加1,这个可以用于计时
    {
        LL_TIM_ClearFlag_UPDATE(TIM1); // 清标志
        tim1_count++;                  // 计ms数
    }
    // 是否有新的显示命令,就将显示参数复制,用于显示
    if (d.new_data_in)
    {
        display_nbit = d.display_nbit;
        head = d.head;
        tail = d.tail;
        text_buf = d.text_buf;
        left_shift_time = d.left_shift_time;
        display_nbit_offset = 0;
        d.new_data_in = 0;
    }
    // 刷新一行数据
    if (display_nbit)
    {
        uint8_t *pStart = (uint8_t *)((uint32_t)display_buf + r * BUF_SIZE * 2); // 本行循环显存的开始地址
        uint8_t *pCur = (uint8_t *)((uint32_t)pStart + head % (BUF_SIZE * 2));   // 本行循环显存要移入显示屏的数据的开始地址
        uint8_t *pEnd = (uint8_t *)((uint32_t)pStart + BUF_SIZE * 2);            // 本行循环显存的结束的下一个地址
        uint8_t display_data = *pCur;                                            // 读取第一个8位移位数据
        uint16_t n = 0;                                                          // 计数到7就需要再读一个字节
        uint16_t has_nbit = (tail - head) * 8;                                   // 显存中有多少列的点阵数据
        uint16_t n_bit = display_nbit;                                           // 要显示多少位
        uint16_t zero_bit = 0;
        // 判断要移入多少个0填充
        if (display_nbit + display_nbit_offset > has_nbit)
        {
            zero_bit = display_nbit + display_nbit_offset - has_nbit; // 要显示的0的列的个数
            n_bit = has_nbit - display_nbit_offset;                   // 要显示的列数比显存中的还多,先显示显存中的,接着显示0
        }
        if (n_bit < SCREEN_WIDTH)
        {
            zero_bit = SCREEN_WIDTH - n_bit;
        }
        display_data >>= display_nbit_offset; // 去掉要跳过的位数
        n = display_nbit_offset;              // 用于计数剩下的位
        // 移入已有数据
        while (n_bit--)
        {
            R1 = G1 = display_data; // 移位
            CLK = 0;                // 时针
            CLK = 1;                // 时针
            display_data >>= 1;
            if (++n == 8)
            {
                n = 0;
                pCur = (pCur + 1 == pEnd) ? pStart : pCur + 1;
                display_data = *pCur;
            }
        }
        // 不够的补位的0移入
        R1 = G1 = 0; // 移位
        while (zero_bit--)
        {
            CLK = 0; // 时针
            CLK = 1; // 时针
        }
        DisplayOFF;          // 关显示
        D = r >> 3;          // 设置行
        C = r >> 2;          // 设置行
        B = r >> 1;          // 设置行
        A = r >> 0;          // 设置行
        Latch;               // 锁存
        DisplayON;           // 开显示
        r++;                 // 换行
        r = r == 16 ? 0 : r; // 到15行后重新到第1行
    }
    // 自动读入字符到显存,显存是循环队列,自动决定位置
    if (text_buf && *text_buf)
    {
        // 是否还有空出的显存位置读入字符
        if (tail - head < BUF_SIZE * 2 - 1)
        {
            // 计算数据要存放的位置
            uint8_t position = tail % (BUF_SIZE * 2);
            uint8_t *p = (uint8_t *)display_buf + position;
            // 要读一个汉字
            if (*(uint8_t *)text_buf >= 0xA1)
            {
                // 计算芯片地址,并设置好flash芯片
                uint32_t addr = ((*(uint8_t *)text_buf - 0xA1) * 94 + (*(uint8_t *)(text_buf + 1) - 0xA1)) * 32;
                while (flash_read_state1() & W25Q32_BUSY) // 等待忙结束
                {
                }
                FLASH_CS = 0;
                flash_byte_write(0x03);       // 读数据指令
                flash_byte_write(addr >> 16); // 发送24位地址
                flash_byte_write(addr >> 8);
                flash_byte_write(addr);
                // 存储16X16左边一半点阵到显存
                for (uint8_t i = 0; i < 16; i++)
                {
                    *p = flash_byte_read();
                    p += BUF_SIZE * 2;
                }
                // 存储16X16右边一半点阵到显存
                p = position == BUF_SIZE * 2 - 1 ? (uint8_t *)display_buf : (uint8_t *)display_buf + position + 1;
                for (uint8_t i = 0; i < 16; i++)
                {
                    *p = flash_byte_read();
                    p += BUF_SIZE * 2;
                }
                tail += 2;
                text_buf += 2;
                // 读完数据,取消片选
                FLASH_CS = 1;
            }
            // 读一个ascii字符
            else
            {
                uint8_t *pAscii = (uint8_t *)&ascii_8x16_font[(*(uint8_t *)text_buf - 0x20) * 16];
                for (uint8_t i = 0; i < 16; i++)
                {
                    *p = *pAscii++;
                    p += BUF_SIZE * 2;
                }
                tail += 1;
                text_buf++;
            }
        }
    }
    // 根据参数决定是否连续左移显示更多字符
    if (left_shift_time && (tim1_count % left_shift_time == 0))
    {
        if (tail > head)
        {
            if (display_nbit_offset < 8 - 1)
            {
                display_nbit_offset++;
            }
            else
            {
                display_nbit_offset = 0;
                head++;
            }
        }
        else
        {
            // 显示完成之后,没有新显示任务,就重新显示原来的任务
            if (d.new_data_in == 0)
            {
                for (uint8_t i = 0; i < BUF_SIZE * 2; i++)
                {
                    clear_8x16_to_display_buf(i);
                }
                head = d.head;
                tail = d.tail;
                text_buf = d.text_buf;
            }
        }
    }
}